package cn.wolfcode.luowowo.search.service.impl;

import cn.wolfcode.luowowo.search.query.HotelSearchQuery;
import cn.wolfcode.luowowo.search.repositoty.HotelTemplateRepository;
import cn.wolfcode.luowowo.search.service.IHotelTemplateSearchService;
import cn.wolfcode.luowowo.search.template.HotelTemplate;
import cn.wolfcode.luowowo.search.template.HotelTemplate;
import cn.wolfcode.luowowo.search.vo.StatisVO;
import com.alibaba.dubbo.config.annotation.Service;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.composite.*;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class HotelTemplateSearchServiceImpl implements IHotelTemplateSearchService {

    //es 依然使用spring-data-jpa

    //repository 封装了template
    //template 封装了 client

    @Autowired
    private HotelTemplateRepository repository;
    @Autowired
    private ElasticsearchTemplate template;
    @Autowired
    private TransportClient client;  //java操作es的第三方客户端


    @Override
    public List<StatisVO> conditionGroupByQo(String groupName,String nameField,HotelSearchQuery qo) {
        if(StringUtils.hasLength(qo.getDestName())){
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.must(QueryBuilders.termQuery("destName",qo.getDestName()));
            return conditionGroup(groupName,nameField,boolQueryBuilder);
        }else {
            return conditionGroup(groupName,nameField,null);
        }
    }

    @Override
    public List<StatisVO> conditionGroupByQoChange(String groupName,String nameField,HotelSearchQuery qo) {
        return conditionGroup(groupName,nameField,getBoolQueryBuilder(qo));
    }

    @Override
    public void saveOrUpdate(HotelTemplate template) {
        repository.save(template);
    }

    @Override
    public HotelTemplate findByName(String keyword) {
        return repository.findByName(keyword);
    }

    @Override
    public List<HotelTemplate> findByDestName(String name) {
        return repository.findByDestName(name);
    }

    @Override
    public List<HotelTemplate> findByDestId(Long id) {
        return repository.findByDestId(id);
    }

    @Override
    public List<StatisVO> conditionGroup(String groupName, String nameField, QueryBuilder queryBuilder){
        List<StatisVO> statisVOList = new ArrayList<>();
        List<CompositeValuesSourceBuilder<?>> sourceBuilders = new ArrayList<>();
        TermsValuesSourceBuilder nameSource = new TermsValuesSourceBuilder("name");
        nameSource.field(nameField);
        nameSource.missingBucket(true);
        sourceBuilders.add(nameSource);
        CompositeAggregationBuilder compositeAggregationBuilder = new CompositeAggregationBuilder(groupName,sourceBuilders);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withIndices(HotelTemplate.INDEX_NAME);
        builder.withTypes(HotelTemplate.TYPE_NAME);
        builder.addAggregation(compositeAggregationBuilder);
        //size不能为0
        builder.withPageable(PageRequest.of(0,1));
        if(queryBuilder != null){
            builder.withQuery(queryBuilder);
        }
        AggregatedPage<HotelTemplate> search = (AggregatedPage<HotelTemplate>) repository.search(builder.build());
        InternalComposite composite = search.getAggregations().get(groupName);
        StatisVO statisVO = null;
        for (CompositeAggregation.Bucket bucket : composite.getBuckets()) {
            statisVO = new StatisVO();
            if(bucket.getKey().get("name") != null) {
                statisVO.setName(bucket.getKey().get("name").toString());
                statisVO.setCount(bucket.getDocCount());
                statisVOList.add(statisVO);
            }
        }
        statisVOList.sort(new Comparator<StatisVO>() {
            @Override
            public int compare(StatisVO o1, StatisVO o2) {
                return (int) (o2.getCount() - o1.getCount());
            }
        });
        return statisVOList;
    }

    @Override
    public Page<HotelTemplate> searchHotel(HotelSearchQuery qo){
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(qo);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(boolQueryBuilder);
        return  repository.search(builder.build());
    }

    private BoolQueryBuilder getBoolQueryBuilder(HotelSearchQuery qo) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(StringUtils.hasLength(qo.getDestName())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("destName", qo.getDestName()));
        }
        if(StringUtils.hasLength(qo.getBrand())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("brand", qo.getBrands()));
        }
        if(StringUtils.hasLength(qo.getFacility())) {
            for (String facility : qo.getFacilities()) {
                boolQueryBuilder.must(QueryBuilders.termsQuery("facilities", facility));
            }
        }
        if(StringUtils.hasLength(qo.getService())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("services", qo.getService()));
        }
        if(StringUtils.hasLength(qo.getThemeName())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("themeName", qo.getThemeName()));
        }
        if(StringUtils.hasLength(qo.getLevel())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("level", qo.getLevels()));
        }
        if(StringUtils.hasLength(qo.getTypeName())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("typeName", qo.getTypeNames()));
        }
        return boolQueryBuilder;
    }

    @Override
    public List<Map<String,Object>> getAvgPrice(HotelSearchQuery qo){
        List<Map<String,Object>> mapList = new ArrayList<>();
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.addAggregation(
                AggregationBuilders.terms("groupByLevel").field("level")
                        .subAggregation(AggregationBuilders.avg("avgPrice").field("minPrice"))
        );
        builder.withQuery(this.getBoolQueryBuilder(qo));
        AggregatedPage<HotelTemplate> page = (AggregatedPage<HotelTemplate>)
                repository.search(builder.build());
// 获取自定义的分组字段
        LongTerms level = (LongTerms) page.getAggregation("groupByLevel");
        for (LongTerms.Bucket bucket : level.getBuckets()) {
            InternalAvg avgPrice = bucket.getAggregations().get("avgPrice");
            Map<String,Object> map = new HashMap<>();
            map.put("level",bucket.getKey());
            map.put("avgPrice",avgPrice.getValue());
            mapList.add(map);
        }
        return mapList;
    }
}
